/* Vu.c: An auto-detecting file viewer for the Psion Series 3

	Currently views:
	Hex dump of files					(hex.c)
	Window Server PIC Bitmaps		(pic.c)
	IMG/APP file data					(img.c)
	ASCII text files					(text.c)
	Font FON files						(font.c)

	To be added:
	Other picture types (with conversion to and from PIC)
	DBF files: General (OPL), Data, and Agenda
	Word WRD files
	Embedded Addfiles in img files of types above
	Listing and viewing files in archives

	1993 J. C. Roux
 */

#include "global.h"
#include "fns.h"
#include "vu-res.h"			/* general resource ids */

GLDEF_D TEXT **				_cmds ;
GLDEF_D H_MENU_DATA *		_mdata ;

LOCAL_D DEFNS					defns ;			/* Type specific definitions */
LOCAL_D DEFN_FN				DefnFunctions[NUM_FILE_TYPES] ;

LOCAL_C VOID GetFileInfo(VOID) {
	P_INFO			info ;

	if ( file.pfcb != NULL )
		p_close(file.pfcb) ;
	file.pfcb = NULL ;
	file.size = 0 ;

	f_fparse(file.name, NULL, file.name, NULL) ;
	f_leave( p_finfo(file.name, &info) ) ;
	file.size = info.size ;
}

LOCAL_C INT AutoDetect(INT filetype) {
	UINT				bytes_to_read ;
	BYTE				buf[TEST_LENGTH] ;
	INT				iLp ;

	bytes_to_read = (UINT) MIN(file.size, TEST_LENGTH) ;

	if ( file.pfcb != NULL )
		p_close(file.pfcb) ;
	file.pfcb = NULL ;

	f_open(	&file.pfcb,
				file.name,
				P_FSTREAM | P_FOPEN | P_FSHARE ) ;

	f_read(	file.pfcb,
				buf,
				bytes_to_read ) ;

	p_close(file.pfcb) ;
	file.pfcb = NULL ;

	if ( filetype == FT_HEX ) {
		for ( iLp = 0 ; iLp < NUM_FILE_TYPES ; iLp++ ) {
			DefnFunctions[iLp](&defns) ;
			if (	defns.pfRecognize != NULL &&
					defns.pfRecognize(buf, bytes_to_read) )
				return(iLp) ;
		}
	}
	else {
		DefnFunctions[filetype](&defns) ;
		if (	defns.pfRecognize == NULL ||
				defns.pfRecognize(buf, bytes_to_read) )
			return(filetype) ;
	}

	return(FT_HEX) ;
}

LOCAL_C VOID CleanUp() {
	if ( file.pfcb != NULL ) {
		p_close(file.pfcb) ;
		file.pfcb = NULL ;
	}
	p_exit(E_APP_NONE) ;
}

LOCAL_C VOID DisplayFileInfo(VOID) {
	TEXT				text[128] ;
	H_DI_TEXT		dialog_text = { NULL, H_DTEXT_ALIGN_LEFT } ;
	TEXT *			str = NULL ;

	/* Display for all files:
		Filename
		Size: xxxx bytes
	*/
	dialog_text.str = text ;

	ReadResource(RS_SIZE_BYTES, &str) ;
	p_atos(&dialog_text.str[1], str, file.size) ;
	p_free(str) ;

	dialog_text.str[0] = p_slen(&dialog_text.str[1]) ;

	if (	uOpenDialog(file.name) ||
			uAddDialogItem(H_DIALOG_TEXT, NULL, &dialog_text) )
		return ;

	if ( defns.pfFileInfo != NULL )
		if ( p_enter1(defns.pfFileInfo) )
			return ;

	uRunDialog() ;
}

LOCAL_C INT DoMenu(INT keycode) {
	if ( 	_cmds == NULL ||
			_mdata == NULL ||
			defns.pfMenuExec == NULL )
		return(E_APP_NO_MENU) ;

	if ( keycode == W_KEY_MENU ) {
		keycode = uPresentMenus() ;
		if ( keycode <= 0 )
			return(keycode) ;
	}

	keycode = uLocateCommand(keycode & ~W_SPECIAL_KEY) ;
	if ( keycode >= 0 )
		return( p_enter2(defns.pfMenuExec, keycode) ) ;
}

LOCAL_C VOID ViewLoop(UINT file_type) {
	WMSG_KEY				key ;
	P_EXTENT				extent ;

	DefnFunctions[file_type](&defns) ;

	_cmds = defns.pcmds ;
	_mdata = defns.pmdata ;

	gfx.style = defns.style ;
	metrics.disp_size = metrics.scr_size ;

	if ( defns.flags & STATUS_BAR ) {
		/* Status bar at bottom of screen */
		MakeStatusBar(	&gfx.status_bar,
							WS_FONT_SYSTEM,
							G_STY_NORMAL,
							&metrics.scr_size ) ;
		ShowBar(&gfx.status_bar) ;

		GetBarExtent(&gfx.status_bar, &extent) ;
		metrics.disp_size.y -= extent.height ;
	}

	if ( defns.flags & SCROLL_Y ) {
		/* Vertical scroller at right of screen */
		MakeScrollBar(	&gfx.scroll_y,
							metrics.scr_size.x - SCROLL_WID,
							0,
							metrics.disp_size.y,
							FALSE ) ;
		ShowBar(&gfx.scroll_y) ;

		GetBarExtent(&gfx.scroll_y, &extent) ;
		metrics.disp_size.x -= extent.width ;
	}

	if ( defns.flags & SCROLL_X ) {
		/* Horizontal scroller at bottom of screen */
		MakeScrollBar(	&gfx.scroll_x,
							0,
							metrics.disp_size.y - SCROLL_WID,
							metrics.disp_size.x,
							TRUE ) ;
		ShowBar(&gfx.scroll_x) ;

		GetBarExtent(&gfx.scroll_x, &extent) ;
		metrics.disp_size.y -= extent.height ;
	}

	gfx.super = (defns.flags & SUPER) != 0 ;

	glob.main = FALSE ;
	glob.quit = FALSE ;

	if ( defns.pfInit != NULL )
		if ( p_enter1(defns.pfInit) ) {
			Res_ErrorString(RS_ERR_LOAD) ;
			glob.main = TRUE ;
		}

	while (	!glob.main &&
				!glob.quit ) {

		/* Update Display */
		if ( defns.pfUpdate != NULL )
			if ( p_enter1(defns.pfUpdate) ) {
				Res_ErrorString(RS_ERR_LOAD) ;
				glob.main = TRUE ;
				break ;
			}

		/* Get a keypress */
		uGetKey(&key) ;

		if ( key.keycode == CONS_EVENT_COMMAND ) {
			/* System message */
			glob.quit = TRUE ;
		}
		else if (	key.keycode & W_SPECIAL_KEY ||
						key.keycode == W_KEY_MENU ) {
			/* Menu Key or Menu Hot Key */
			if ( DoMenu(key.keycode) )
				glob.main = TRUE ;
		}
		else {

			/* Standard Keys */
			switch (key.keycode) {

				case W_KEY_ESCAPE :
					glob.main = TRUE ;
					break ;

				case W_KEY_HELP :
					if ( key.modifiers == W_CTRL_MODIFIER )
						SystemHelp() ;
					else
						Help(defns.nHelp, RH_MAIN) ;
					break ;

				case W_KEY_TAB :
					DisplayFileInfo() ;
					break ;

				default :
					/* Other keys are passed through */
					if ( defns.pfPlainKey != NULL )
						if ( p_enter2(defns.pfPlainKey, &key) )
							glob.main = TRUE ;
					break ;
			}
		}
	}

	if ( defns.flags & STATUS_BAR )
		HideBar(&gfx.status_bar) ;

	if ( defns.flags & SCROLL_X )
		HideBar(&gfx.scroll_x) ;

	if ( defns.flags & SCROLL_Y )
		HideBar(&gfx.scroll_y) ;

	if ( defns.pfCleanUp != NULL )
		p_enter1( defns.pfCleanUp ) ;
}

LOCAL_C VOID TryHex(VOID) {
	BOOL		inhex = FALSE ;

	Check( p_enter5(Query, RS_ERR_UNKNOWN, RS_CONTINUE, RS_ABORT, &inhex) ) ;
	if ( inhex )
		ViewLoop(FT_HEX) ;
}

#pragma save, ENTER_CALL

LOCAL_C INT MainLoop(VOID) {
	P_INFO				info ;
	H_DI_CHOICE			dialog_choice ;
	H_DI_FSEL			fsel ;
	UWORD					choice = 1 ;
	UWORD					help ;
	INT					iLp ;
	INT					filetype ;

	if (	p_getenv(VU_ENV, file.name) ||
			p_finfo(file.name, &info) )
		p_scpy(file.name, "LOC::M:\\*.*") ;

	fsel.fname = file.name ;
	fsel.flags = H_FILE_PICK_SELECTOR ;

	while ( !glob.quit ) {

		ZTStoBCS(file.name) ;
		Res_OpenDialog(RS_VERSION) ;
		Res_AddDialogItem(H_DIALOG_FSEL, RS_FILE, &fsel) ;
		f_leave( uBeginDCL(&dialog_choice) ) ;

		f_leave( uGrowDCL(&dialog_choice, "Auto") ) ;
		for ( iLp = 0 ; iLp < NUM_FILE_TYPES ; iLp++ )
			f_leave( uGrowDCL(&dialog_choice, File_Types[iLp]) ) ;

		Res_AddDCL(RS_TYPE, &choice, &dialog_choice) ;
		help = 1 ;
		Res_AddChoiceList(RS_HELP_NEEDED, RS_NO, RS_YES, &help) ;

		if ( !Res_RunDialog() ) {
			/* Escape at main dialog - quit */
			p_leave(E_APP_NONE) ;
		}

		BCStoZTS(file.name) ;
		p_setenv(VU_ENV, file.name) ;

		if ( help == 2 ) {
			/* User wants help for that file type */
			if ( choice == 1 )
				Help(RH_FILE_TYPES, RH_MAIN) ;
			else {
				DefnFunctions[choice - 2](&defns) ;
				Help(defns.nHelp, RH_MAIN) ;
			}
				
		}
		else {
			GetFileInfo() ;

			if ( choice != 2 )
				filetype = AutoDetect(choice == 1 ? FT_HEX : choice - 2) ;

			if (	choice == 2 ||
					filetype )
				ViewLoop(filetype) ;
			else
				TryHex() ;
		}
	}

	return(E_APP_NONE) ;
}

LOCAL_C INT SpecificInit(VOID) {
	G_FONT_INFO		fontinfo ;

	p_unmarka() ;
	hCrackCommandLine() ;

	OpenResource(DatCommandPtr) ;

	gFontInfo(WS_FONT_SYSTEM, G_STY_NORMAL, &fontinfo) ;
	metrics.scr_size = wserv_channel->conn.info.pixels ;
	metrics.text_ascent = fontinfo.ascent ;
	metrics.text_height = fontinfo.height ;

	gfx.wdata.flags = W_WIN_NO_REDRAW ;
	gfx.wdata.extent.tl.x = 0 ;
	gfx.wdata.extent.tl.y = 0 ;
	gfx.wdata.extent.width = metrics.scr_size.x ;
	gfx.wdata.extent.height = metrics.scr_size.y ;
	gfx.wdata.background = W_WIN_BACK_BITMAP ;

	gfx.win_id = wCreateWindow(	0,
											W_WIN_NO_REDRAW | W_WIN_EXTENT | W_WIN_BACKGROUND,
											&gfx.wdata,
											(UWORD) &gfx.wdata ) ;
	wInitialiseWindowTree(gfx.win_id) ;

/* MODIFY START */

	DefnFunctions[FT_HEX] = HexDefns ;
	DefnFunctions[FT_PIC] = PicDefns ;
	DefnFunctions[FT_IMG] = ImgDefns ;
	DefnFunctions[FT_TXT] = TextDefns ;
	DefnFunctions[FT_FON] = FontDefns ;

/* MODIFY END */

	Help(RH_ABOUT, RH_MAIN) ;

	return(E_APP_NONE) ;
}

#pragma restore

/* Global routines */

GLDEF_C VOID main(VOID) {
	uCommonInit() ;
	wDisableLeaves(FALSE) ;
	Check( p_enter1(SpecificInit) ) ;
	Check( p_enter1(MainLoop) ) ;
	CleanUp() ;
}

